﻿using FileEncoding.Helper;
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace FileEncoding
{
    public partial class Form1 : Form
    {
        [DllImport("kernel32.dll")]
        public static extern Boolean FreeConsole();

        // 选择文件类型的窗体
        private Form2 _selectTypeForm;

        // 选择完文件类型的回调
        public static Action<string> OnChooseEnd;

        private FolderBrowserDialog _folderDialog = new FolderBrowserDialog();
        private ImageList _imgList;

        private List<string> _files;

        private bool _isScaning = false;
        private bool _isTranscoding = false;

        private bool _errorEncoding = false;

        public Form1()
        {
            InitializeComponent();
            InitEvents();

            // 初始化Form2
            if (_selectTypeForm == null)
            {
                _selectTypeForm = new Form2();
            }

            _selectTypeForm.RefreshTypesLabel();
            cbEncoding.SelectedIndex = 4;
        }

        private void InitEvents()
        {
            OnChooseEnd += OnChooseEndShow;
        }

        private void OnChooseEndShow(string v)
        {
            labelChoosedTypes.Text = "已选择类型：" + v;
        }

        // 选择文件类型按钮点击
        private void btnChooseFileType_Click(object sender, EventArgs e)
        {
            _selectTypeForm.StartPosition = FormStartPosition.CenterParent;
            _selectTypeForm.ShowDialog();
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
#if DEBUG
            FreeConsole();
#endif
        }

        // 选择文件夹
        private void btnSelectFolder_Click(object sender, EventArgs e)
        {
            _folderDialog.ShowDialog(this);
            string selectedPath = _folderDialog.SelectedPath;
            if (string.IsNullOrEmpty(selectedPath))
            {
                MessageBox.Show("选择的路径为空！");
                return;
            }
            tbFolder.Text = selectedPath;
        }

        // 开始扫描文件
        private void btnStartScan_Click(object sender, EventArgs e)
        {

            string folder = tbFolder.Text.Trim();

            // 获取搜索匹配字符串 
            string pattern = "*.*";

            Console.WriteLine(pattern);
            if (_selectTypeForm.extensions.Count < 1)
            {
                MessageBox.Show("请选择需要扫描的文件类型！", "提示");
                return;
            }
            else if (string.IsNullOrEmpty(folder))
            {
                MessageBox.Show("请选择或填写的文件夹路径！", "提示");
                return;
            }


            Invoke(new MethodInvoker(() =>
            {
                // 开启终止扫描按钮
                SetBtnsEnabled(false);

                labelWarning.Visible = false;
                // 开启提示
                labelScaning.Visible = true;
                labelProgress.Visible = true;
                pbTrans.Visible = true;

                labelScaning.Text = "开始扫描文件...";
                // 清除listView
                listViewFiles.Items.Clear();
            }));

            try
            {
                var treadDelegate = new ThreadStart(delegate
                {
                    SearchOption searchOption = cbChildFolder.Checked ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
                    // 找出所有匹配文件
                    _files = GetFiles(folder, pattern, searchOption)
                        .Where(f => _selectTypeForm.extensions.Contains(Path.GetExtension(f))).ToList();

                    if (_files == null)
                    {
                        return;
                    }
                    _isScaning = true;

                    Invoke(new MethodInvoker(() =>
                    {
                        // 设置进度条默认值
                        pbTrans.Value = 0;
                        pbTrans.Maximum = _files.Count;
                    }));

                    for (int i = 0; i < _files.Count; ++i)
                    {
                        string path = _files[i];
                        string extension = Path.GetExtension(path);

                        AddListViewFilesItem(i, _files, path, folder, extension);
                    }

                    // 排序

                    Invoke(new MethodInvoker(() =>
                    {
                        labelScaning.Text = "扫描文件完成！";
                        SetBtnsEnabled(true);
                        _isScaning = false;
                    }));
                });

                new Thread(treadDelegate).Start();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "异常信息");
            }
        }

        private void SetBtnsEnabled(bool value)
        {
            if (!_errorEncoding)
            {
                btnTranscoding.Enabled = value;
            }
            else
            {
                MessageBox.Show("指定的文件夹路径下含有无法识别的文件编码！", "提示");
            }

            btnStartScan.Enabled = value;
            btnSelectFolder.Enabled = value;
            btnChooseFileType.Enabled = value;
        }

        // 获取指定路径下的所有文件
        private List<string> GetFiles(string folder, string pattern, SearchOption searchOption)
        {
            string[] strArr = null;

            try
            {
                strArr = Directory.GetFiles(folder, pattern, searchOption);
            }
            catch (UnauthorizedAccessException ex)
            {
                MessageBox.Show("没有打开文件夹的权限！", "提示");
            }
            catch (DirectoryNotFoundException ex)
            {
                MessageBox.Show("指定的文件夹路径无效！", "提示");
            }
            catch (ArgumentException ex)
            {
                MessageBox.Show("参数无效！" + ex.Message, "提示");
            }
            catch (PathTooLongException ex)
            {
                MessageBox.Show("指定的文件夹路径超出系统定义的最大长度！", "提示");
            }
            catch
            {
                MessageBox.Show("无法打开文件夹！", "提示");
            }

            if (strArr != null)
            {
                return strArr.ToList();
            }

            return null;
        }

        // 添加子物体到ListViewFiles中
        private void AddListViewFilesItem(int i, List<string> files, string path, string folder, string extension)
        {
            Invoke(new MethodInvoker(() =>
            {
                var sw = new Stopwatch();
                sw.Start();
                Console.WriteLine(path);

                path = "//?/" + path;

                //FileStream fs = File.OpenRead(path);


                //var buffer = new byte[fs.Length];
                //fs.Read(buffer, 0, buffer.Length);
                //fs.Close();
                //fs.Dispose();

                var buffer = File.ReadAllBytes(path);

                string encodingDes = "";
                // Encoding encoding = GetEncoding(buffer, out encodingDes);
                Encoding encoding = EncodingHelper.GetEncoding(buffer, path, out encodingDes);

                if (encodingDes == "Default")
                {
                    Console.WriteLine("Error: encoding des is default");
                }

                string str = File.ReadAllText(path);

                if (encoding == null)
                {
                    _errorEncoding = true;
                    encodingDes = "无法识别，请检查";
                    MessageBox.Show(string.Format("无法识别此文件({0})编码!", path.Remove(0, 4), "错误"));
                }

                TimeSpan ts = sw.Elapsed;
                string elapsedTime = string.Format("{0}ms", ts.Milliseconds);

                var item = new ListViewItem();
                if (!_imgList.Images.ContainsKey(extension))
                {
                    string des;
                    Icon largeIcon;
                    Icon smallIcon;
                    FileHelper.GetExtsIconAndDescription(extension, out largeIcon, out smallIcon, out des);

                    if (_imgList != null)
                    {
                        _imgList.Images.Add(extension, smallIcon);  // 指定键值对
                    }
                }

                item.ImageKey = extension;

                item.Text = path.Replace(folder, "").TrimStart('\\').Remove(0, 4);
                item.SubItems.Add(encodingDes);
                item.SubItems.Add("");
                item.SubItems.Add(elapsedTime);

                listViewFiles.BeginUpdate();

                if (encodingDes == "无法识别，请检查")
                {
                    listViewFiles.Items.Insert(0, item);
                }
                else
                {
                    listViewFiles.Items.Add(item);
                }

                listViewFiles.Items[listViewFiles.Items.Count - 1].EnsureVisible();
                listViewFiles.EndUpdate();

                if (pbTrans.Value + (int)Math.Ceiling(100f / _files.Count) > pbTrans.Maximum)
                {
                    pbTrans.Value = pbTrans.Maximum;
                }
                else
                {
                    pbTrans.Value += (int)Math.Ceiling(100f / _files.Count);
                }

            }));
        }

        // 开始转换文件编码
        private void btnTranscoding_Click(object sender, EventArgs e)
        {
            Encoding encoding;
            encoding = GetSelectedEncoding();
            if (encoding == null)
            {
                return;
            }

            string folder = tbFolder.Text.Trim();
            var treadDelegate = new ThreadStart(delegate
            {
                Invoke(new MethodInvoker(() =>
                {
                    labelScaning.Text = "正在转换文件编码...";
                    SetBtnsEnabled(false);
                    _isTranscoding = true;
                    listViewFiles.Items.Clear();
                }));

                if (_files != null)
                {
                    Invoke(new MethodInvoker(() =>
                    {
                        // 设置进度条默认值
                        pbTrans.Value = 0;
                        pbTrans.Maximum = _files.Count;
                    }));
                    for (int i = 0; i < _files.Count; i++)
                    {
                        Invoke(new MethodInvoker(() =>
                        {
                            var sw = new Stopwatch();
                            sw.Start();

                            string path = @"\\?\" + _files[i];  // "//?/"处理文件路径过长问题
                            string extension = Path.GetExtension(path);

                            // 路径设置"//?/"
                            FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read);

                            var buffer = new byte[fs.Length];
                            fs.Read(buffer, 0, buffer.Length);
                            fs.Close();
                            fs.Dispose();
                            string encodingDes = "";
                            //   var fileEncoding = GetEncoding(buffer, out encodingDes);
                            var fileEncoding = EncodingHelper.GetEncoding(buffer, path, out encodingDes);

                            if (encodingDes == "Default")
                            {
                                Console.WriteLine("Error: encoding des is default");
                            }

                            var result = "无需转换";
                            Console.WriteLine("Encoding: {0}", encoding);
                            Console.WriteLine("FileEncoding: {0}", fileEncoding);
                            if (!Equals(fileEncoding, encoding))
                            {
                                try
                                {
                                    string txt = GetTxt(buffer, fileEncoding);
                                    Console.WriteLine(encoding);
                                    WriteTxt(path, txt, encoding);
                                    result = string.Format("转换成{0}成功", encoding.BodyName);
                                }
                                catch
                                {
                                    result = string.Format("转换成{0}失败", encoding.BodyName);
                                }
                            }

                            sw.Stop();
                            TimeSpan ts = sw.Elapsed;
                            string elapsedTime = string.Format("{0}ms", ts.Milliseconds);

                            var item = new ListViewItem();
                            if (!_imgList.Images.ContainsKey(extension))
                            {
                                string des;
                                Icon largeIcon;
                                Icon smallIcon;
                                FileHelper.GetExtsIconAndDescription(extension, out largeIcon, out smallIcon, out des);

                                if (_imgList != null)
                                {
                                    _imgList.Images.Add(extension, smallIcon); //指定键值对 
                                }
                            }
                            item.ImageKey = extension; //设定键值对

                            item.Text = path.Replace(folder, "").TrimStart('\\').Remove(0, 4);
                            item.SubItems.Add(encodingDes);
                            item.SubItems.Add(result);
                            item.SubItems.Add(elapsedTime);

                            listViewFiles.BeginUpdate();
                            listViewFiles.Items.Add(item);
                            listViewFiles.Items[listViewFiles.Items.Count - 1].EnsureVisible();
                            listViewFiles.EndUpdate();

                            if (pbTrans.Value + (int)Math.Ceiling(100f / _files.Count) > pbTrans.Maximum)
                            {
                                pbTrans.Value = pbTrans.Maximum;
                            }
                            else
                            {
                                pbTrans.Value += (int)Math.Ceiling(100f / _files.Count);
                            }
                        }));
                    }

                    Invoke(new MethodInvoker(() =>
                    {
                        labelScaning.Text = "转换文件编码完成！";
                        _isTranscoding = false;
                        SetBtnsEnabled(true);
                    }));
                }
            });

            new Thread(treadDelegate).Start();
        }

        private void WriteTxt(string path, string txt, Encoding encoding)
        {
            if (File.Exists(path))
            {
                File.Delete(path);
            }

            if (Equals(encoding, Encoding.UTF8))
            {
                /* utf8 和 utf8EmitBOM的区别：
                   utf8 preamble:  
                   utf8EmitBOM:[EF] [BB] [BF]
                */
                if (encoding.GetPreamble().Length > 0)
                {
                    encoding = new UTF8Encoding(true);
                }
                else
                {
                    encoding = new UTF8Encoding(false);
                }
            }

            StreamWriter sw = new StreamWriter(path, false, encoding);
            sw.Write(txt);
            sw.Close();
            sw.Dispose();
        }

        //private string GetTxt(byte[] buffer)
        //{
        //    string a = "";
        //    return GetTxt(buffer, GetEncoding(buffer, out a));
        //}

        private string GetTxt(byte[] buffer, Encoding encoding)
        {
            if (Equals(encoding, Encoding.UTF8))
            {
                return encoding.GetString(buffer, 3, buffer.Length - 3);
            }
            if (Equals(encoding, Encoding.BigEndianUnicode) || Equals(encoding, Encoding.Unicode))
            {
                return encoding.GetString(buffer, 2, buffer.Length - 2);
            }
            return encoding.GetString(buffer);
        }

        private Encoding GetSelectedEncoding()
        {
            Encoding encoding = null;
            switch (cbEncoding.SelectedIndex)
            {
                case 0:
                    encoding = Encoding.Default;
                    break;
                case 1:
                    encoding = Encoding.Unicode;
                    break;
                case 2:
                    encoding = Encoding.BigEndianUnicode;
                    break;
                case 3:
                    encoding = new UTF8Encoding(false);
                    break;
                case 4:
                    encoding = new UTF8Encoding(true);
                    break;
                default:
                    MessageBox.Show("请选择要转换的编码！", "提示");
                    break;
            }

            return encoding;
        }

        // 当窗体被打开时调用
        private void OnWndLoad(object sender, EventArgs e)
        {
            // 设置默认路径
            tbFolder.Text = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

            // 设置ListView
            listViewFiles.View = View.Details;
            listViewFiles.Columns.Add("文件路径", 500);
            listViewFiles.Columns.Add("编码格式", 100);
            listViewFiles.Columns.Add("转换结果", 100);
            listViewFiles.Columns.Add("耗时");
            listViewFiles.SmallImageList = _imgList;

            // 设置默认转换格式
            cbEncoding.SelectedIndex = 4;

            pbTrans.Visible = false;
            labelProgress.Visible = false;
        }

        // 关闭窗口前的判断
        private void OnFromClosing(object sender, FormClosingEventArgs e)
        {
            Console.WriteLine(_isScaning);
            switch (e.CloseReason)
            {
                case CloseReason.WindowsShutDown:
                    e.Cancel = false;
                    break;
                case CloseReason.ApplicationExitCall:
                case CloseReason.MdiFormClosing:
                case CloseReason.TaskManagerClosing:
                case CloseReason.UserClosing:
                case CloseReason.FormOwnerClosing:
                case CloseReason.None:
                    if (_isScaning || _isTranscoding)
                    {
                        e.Cancel = true;
                    }
                    if (_isScaning)
                    {
                        MessageBox.Show("正在扫描文件中，请勿关闭程序！", "警告");
                    }
                    else if (_isTranscoding)
                    {
                        MessageBox.Show("正在转换文件编码中，请勿关闭程序！", "警告");
                    }
                    break;
                default:
                    break;
            }
        }

        // list view 鼠标点击事件
        private void OnListViewMouseClick(object sender, MouseEventArgs e)
        {
            // 禁止多选
            listViewFiles.MultiSelect = false;

            // 鼠标右击
            if (e.Button == MouseButtons.Right)
            {
                itemCMS.Show(listViewFiles, new Point(e.X, e.Y));
            }
        }

        // listview item 点击事件中的按钮 打开文件
        private void OnOpenFIleTollStripClick(object sender, EventArgs e)
        {
            var selectedFile = listViewFiles.SelectedItems[0];

            string selectedFilePath = null;
            selectedFilePath = _files.Find((x) => { return x.Contains(selectedFile.Text); });

            try
            {
                Process.Start(selectedFilePath);
            }
            catch (System.ComponentModel.Win32Exception ex)
            {
                MessageBox.Show("无法打开！" + ex.Message, "提示");
            }
        }

        // listview item 点击事件中的按钮 打开所在文件夹
        private void OnOpenFilePathToolStripClick(object sender, EventArgs e)
        {
            var selectedFile = listViewFiles.SelectedItems[0];
            var length = Path.GetFileName(selectedFile.Text).Length;
            string selectedFilePath = null;
            selectedFilePath = _files.Find((x) => { return x.Contains(selectedFile.Text); });
            selectedFilePath = selectedFilePath.Remove(selectedFilePath.Length - length, length);

            if (selectedFilePath != null)
            {
                try
                {
                    Process.Start("explorer.exe", selectedFilePath);
                }
                catch (System.ComponentModel.Win32Exception ex)
                {
                    MessageBox.Show("无法打开！" + ex.Message, "提示");
                }
            }
        }
    }
}
